﻿using System;
using System.Collections.Generic;
using System.Linq;
using CommonLibrary.Helpers;
using CommonLibrary.Models;
using DijkstraLibrary;
using FileLibrary.Enums;
using FileLibrary.Models;
using GeneticSharp.Domain.Chromosomes;
using GeneticSharp.Domain.Randomizations;
using GeneticSharpDemo.Data;
using GeneticSharpDemo.Enums;
using GeneticSharpDemo.Models;

namespace GeneticSharpDemo
{
    internal class MSOSChromosome : ChromosomeBase
    {
        DataManage DataManageInstance = DataManage.Instance;

        public int JobAndProcCnt => DataManageInstance.JobAndProcList.Count;
        public int WindowStart => DataManageInstance.NightDutyStartTime;
        public int WindowEnd => DataManageInstance.NightDutyEndTime;
        public int WindowRepairStart => DataManageInstance.RepairDutyStartTime;
        public int DPathTime => DataManageInstance.DPathTime;
        public int SafeTime => DataManageInstance.DPathTime;//两个调车间的最小间隔
        public int SameTrackWindow => 20;//两辆短编车放置在同一股道内
        /// <summary>
        /// 维护所有任务的结束时间,key:JobId
        /// </summary>
        //private int[] jobPreEndTime;
        private Dictionary<int, JobPreEndInfo> jobPreEndInfos = new Dictionary<int, JobPreEndInfo>();
        /// <summary>
        /// 台位计划，需要合并相邻短编车
        /// </summary>
        private Dictionary<int, List<JobAndProcWithPos>> jobAndProcWithPosDictionary =
            new Dictionary<int, List<JobAndProcWithPos>>();

        /// <summary>
        /// 没有成功分配的记录
        /// </summary>
        public List<string> NotScheduleRecord { get; set; }
        /// <summary>
        /// 维护所有任务的安排信息
        /// $"{JobId} {MachId} {ProcId} {StartTime} {EndTime};"
        /// </summary>
        public List<Schedule> ScheduleList { get; set; } = new List<Schedule>();
        ///// <summary>
        ///// 维护所有进路的安排信息
        ///// </summary>
        //public Dictionary<string, Schedule> PathScheduleList { get; set; } = new Dictionary<string, Schedule>();
        /// <summary>
        /// 检修线运用方案的结束时间
        /// </summary>
        public int JobEndTimeMax { get; set; }
        public MSOSChromosome(int count) : base(count * 2)
        {
            GenerateChromosome(count);//编码
            GetJobAndProcWithPosSchedule();//台位运用方案
            GetSchedule();//检修线运用方案
            GetStoreTrackSchedule();//存车线运用方案
            JobEndTimeMax = ScheduleList.Where(y => y.ProcId != (int)TrackTypeEnum.Store).Max(x => x.EndTime);
        }

        /// <summary>
        /// 生成编码
        /// </summary>
        /// <param name="count"></param>
        private void GenerateChromosome(int count)
        {
            Resize(JobAndProcCnt * 2);
            ScheduleList.Clear();
            //PathScheduleList.Clear();
            GetJobPreEndTime();

            //保存已处理任务，优先计算不影响其他计划排布
            var scheduleAdd = new List<int>();

            //检修股道
            for (var index = 0; index < JobAndProcCnt; index++)
            {
                var jobAndProc = DataManageInstance.JobAndProcList[index];
                //根据检修计划配置内容，增加为固定股道，起始终止时间的检修计划增加计划排布
                if (jobAndProc.ProcEndTime > jobAndProc.ProcStartTime && jobAndProc.ProcStartTime > 0 &&
                    !string.IsNullOrEmpty(jobAndProc.ProcTrackName))
                {
                    AddSchedule(jobAndProc.ProcStartTime, jobAndProc.ProcEndTime - jobAndProc.ProcStartTime,
                        jobAndProc.JobInfo.JobId,
                        jobAndProc.Proc.Tracks.First(x => x.Name == jobAndProc.ProcTrackName).Index,
                        jobAndProc.Proc.Index);
                    //按照Job记录信息
                    jobPreEndInfos[jobAndProc.JobInfo.JobId].JobPreEndTime = jobAndProc.ProcEndTime; //记录Job的上一个结束时间
                    jobPreEndInfos[jobAndProc.JobInfo.JobId].JobAndProc = jobAndProc; //记录Job的上一个工序

                    scheduleAdd.Add(index);
                }

                var trackIndex = RandomizationProvider.Current.GetInt(0, jobAndProc.Proc.Tracks.Count); //随机该工序下的股道序号，取股道时为从该列表取得
                if (!string.IsNullOrEmpty(jobAndProc.ProcTrackName)) //根据检修计划配置内容，重新生成检修股道编码
                {
                    trackIndex = jobAndProc.Proc.Tracks.FindIndex(x => x.Name == jobAndProc.ProcTrackName);
                }

                ReplaceGene(index, new Gene(trackIndex)); //Machine Assignment 
            }

            //检修顺序，按照摆放顺序，存放的是任务id
            var jobSeq = RandomizationProvider.Current.GetUniqueInts(JobAndProcCnt, 0, JobAndProcCnt); //Operation Assignment
            if (DataManageInstance.MaintenanceSequences.Count > 0) //检修顺序配置内容，重新整理检修顺序编码
            {
                //jobIdList
                var list = DataManageInstance.MaintenanceSequences
                    .Select(x => DataManageInstance.GetJobIdByTrainId(x.TrainId)).ToList();
                var jobSeqList = new List<int>();
                //把DataManageInstance.JobAndProcList按照配置内容提取出以jobId为单位的检修顺序
                foreach (var jobId in list)
                {
                    foreach (var s in jobSeq)
                    {
                        var jobAndProc = DataManageInstance.JobAndProcList[s];
                        if (jobAndProc.JobInfo.JobId != jobId) continue;
                        jobSeqList.Add(s);
                    }
                }

                //补充没有配置的检修顺序
                foreach (var seq in jobSeq)
                {
                    if (jobSeqList.Contains(seq)) continue;
                    jobSeqList.Add(seq);
                }

                jobSeq = jobSeqList.ToArray();
            }

            var jobSeqUpdateList = new List<int>(scheduleAdd);
            foreach (var seq in jobSeq)
            {
                if (!jobSeqUpdateList.Contains(seq))
                {
                    jobSeqUpdateList.Add(seq);
                }
            }

            jobSeq = jobSeqUpdateList.ToArray();
            for (int i = 0; i < JobAndProcCnt; i++)
            {
                ReplaceGene(i + JobAndProcCnt, new Gene(jobSeq[i]));
            }
        }
        //计算台位运用方案
        //即调度规划的检修股道上动车组的检修顺序
        //长编组的车，1台车占用
        //入库时间接近的两个短编组车，2台车占用
        private void GetJobAndProcWithPosSchedule()
        {
            jobAndProcWithPosDictionary = new Dictionary<int, List<JobAndProcWithPos>>();
            var jobAndProcByTrackDictionary = new Dictionary<int, List<JobAndProc>>();
            //调度一般能理解按股道的检修顺序，即台位检修计划。需要按车型+股道处理计划，得到股道上的台位计划
            var chromosomeList = GetGenes().Select(x => x.Value).Cast<int>().ToList();
            //循环编码下半区进行解码，按照股道存储
            for (int i = 0; i < JobAndProcCnt; i++)
            {
                var nowIndex = chromosomeList[i + JobAndProcCnt]; //按照编码下半区的顺序安排任务
                var jobAndProc = DataManageInstance.JobAndProcList[nowIndex]; //获得任务对象
                var nowJobId = jobAndProc.JobInfo.JobId;
                var nowProcId = jobAndProc.Proc.Index;
                var track = jobAndProc.Proc.Tracks[chromosomeList[jobAndProc.Index]]; //根据股道编码获得股道对象
                var nowMachId = track.Index;
                if (!jobAndProcByTrackDictionary.ContainsKey(nowMachId))
                {
                    jobAndProcByTrackDictionary.Add(nowMachId, new List<JobAndProc>());
                }

                var list = jobAndProcByTrackDictionary[nowMachId];
                list.Add(jobAndProc);
            }
            //计算两列短编车放置在同一股道情况，当两个短编车时间相差60min中可摆放在同一股道内
            foreach (var pair in jobAndProcByTrackDictionary)
            {
                var key = pair.Key;
                var list = pair.Value;
                var track = DataManageInstance.Tracks.First(x => x.Index == key);
                if (!jobAndProcWithPosDictionary.ContainsKey(key))
                {
                    jobAndProcWithPosDictionary.Add(key, new List<JobAndProcWithPos>());
                }

                var jobAndProcWithPos = jobAndProcWithPosDictionary[key];
                var period = 0;
                for (var index = 0; index < list.Count; index++)
                {
                    var jobAndProc1 = list[index];
                    var addProcWithPos = new JobAndProcWithPos()
                    {
                        JobAndProcs = new List<JobAndProc> { jobAndProc1 },
                        PeriodIndex = period++,
                        Track = track,
                    };

                    jobAndProcWithPos.Add(addProcWithPos);
                    if (index != list.Count - 1)
                    {
                        var jobAndProc2 = list[index + 1];
                        if (
                            jobAndProc1.JobInfo.TrainGroupType == TrainGroupTypeEnum.Short
                            && jobAndProc2.JobInfo.TrainGroupType == TrainGroupTypeEnum.Short
                            && track.TrackPosEnum != TrackPosEnum.None
                        )
                        {
                            if (jobAndProc1.JobInfo.StartTime - jobAndProc2.JobInfo.StartTime > 0 &&
                                jobAndProc1.JobInfo.StartTime - jobAndProc2.JobInfo.StartTime < SameTrackWindow)
                            {
                                addProcWithPos.JobAndProcs = new List<JobAndProc> { jobAndProc2, jobAndProc1 };
                                index++;
                            }
                            else if (jobAndProc2.JobInfo.StartTime - jobAndProc1.JobInfo.StartTime > 0 &&
                                     jobAndProc2.JobInfo.StartTime - jobAndProc1.JobInfo.StartTime < SameTrackWindow)
                            {
                                addProcWithPos.JobAndProcs = new List<JobAndProc> { jobAndProc1, jobAndProc2 };
                                index++;
                            }
                        }
                    }
                }
            }
        }

        public override Gene GenerateGene(int geneIndex)
        {
            int value = 0;
            var part = geneIndex / JobAndProcCnt;//分为两个片区
            if (part == 0)
            {
                value = RandomizationProvider.Current.GetInt(0, DataManageInstance.JobAndProcList[geneIndex].Proc.Tracks.Count);
            }
            else if (part == 1)
            {
                value = RandomizationProvider.Current.GetInt(0, JobAndProcCnt);
            }

            return new Gene(value);
        }

        public override IChromosome CreateNew()
        {
            return new MSOSChromosome(JobAndProcCnt);
        }

        public override IChromosome Clone()
        {
            return base.Clone() as MSOSChromosome;
        }

        /// <summary>
        /// 根据调度方案计算存车线使用方案
        /// </summary>
        private void GetStoreTrackSchedule()
        {
            ////当前股道全部调度方案，按照时间排序
            //var nowSchedules = GetSchedulesByJobId(nowMachId);
            ////计算该股道的空闲时间
            //var emptySlots = GetEmptySlotsForMachId(nowSchedules, nowMachId);
            //获取每个任务的执行窗口
            foreach (var j in DataManageInstance.JobInfoDic)
            {
                var jobInfo = j.Value;
                var jobId = jobInfo.JobId;
                var startTime = jobInfo.StartTime > 0 ? jobInfo.StartTime : WindowStart;
                var endTime = jobInfo.EndTime > 0 ? jobInfo.EndTime : WindowEnd;
                //当前任务全部调度方案，按照时间排序
                var nowSchedulesByJobId = GetSchedulesByJobId(jobId);
                //计算该任务的存车时间
                var emptySlotsByJobId = GetEmptySlotsForJobId(nowSchedulesByJobId, jobId, startTime, endTime);
                var find = false;
                //根据存车时间计算存车股道调度方案
                foreach (var jobStoreSlot in emptySlotsByJobId)
                {
                    if (jobStoreSlot.EndTime - jobStoreSlot.StartTime < 10) continue;
                    #region 优先使用该车的已用存车股道

                    var trackUsed = GetSchedulesByJobId(jobId).FirstOrDefault(x => x.ProcId == (int)TrackTypeEnum.Store);
                    if (trackUsed != null)
                    {
                        find = FindScheduleForTrackId(trackUsed.MachId, jobStoreSlot, jobId, jobInfo.TrainGroupType);
                    }

                    #endregion

                    #region 股道均衡，挑选任务最少的股道使用

                    if (find) continue;
                    var tracksSchedule = DataManageInstance.StoreTracks.Select((x, y) => new { GetSchedulesByMachId(x.Index).Count, y }).OrderBy(z => z.Count).ToList();
                    var trackId = DataManageInstance.StoreTracks[tracksSchedule[0].y].Index;
                    find = FindScheduleForTrackId(trackId, jobStoreSlot, jobId, jobInfo.TrainGroupType);

                    #endregion

                    #region 使用股道配置顺序的排布

                    if (find) continue;
                    foreach (var track in DataManageInstance.StoreTracks)
                    {
                        if (find) continue;
                        find = FindScheduleForTrackId(track.Index, jobStoreSlot, jobId, jobInfo.TrainGroupType);
                    }

                    #endregion
                }
            }
        }

        /// <summary>
        /// 将存车任务放入股道
        /// </summary>
        /// <param name="trackId"></param>
        /// <param name="jobStoreSlot"></param>
        /// <param name="jobId"></param>
        /// <param name="trainGroupType"></param>
        /// <returns></returns>
        private bool FindScheduleForTrackId(int trackId, EmptySlot jobStoreSlot, int jobId, TrainGroupTypeEnum trainGroupType)
        {
            var emptySlots = GetEmptySlotsForMachIdByGetSchedules(trackId);
            if (emptySlots.Any(emptySlot => emptySlot.StartTime <= jobStoreSlot.StartTime && emptySlot.EndTime >= jobStoreSlot.EndTime))
            {
                var job = DataManageInstance.JobInfoDic.Values.First(x => x.JobId == jobId);
                var track = DataManageInstance.StoreTracks.First(x => x.Index == trackId);
                var pos = GetFirstShortTrainPos(track, trainGroupType);
                if (!job.DepotIn && jobStoreSlot.StartTime == WindowStart)//非入所
                {
                    AddSchedule(jobStoreSlot.StartTime, jobStoreSlot.EndTime - jobStoreSlot.StartTime - DPathTime,
                        jobId, trackId, (int)TrackTypeEnum.Store, false, (int)pos);
                }
                else if (!job.DepotOut && jobStoreSlot.EndTime == WindowEnd)
                {
                    AddSchedule(jobStoreSlot.StartTime + DPathTime, jobStoreSlot.EndTime - jobStoreSlot.StartTime - DPathTime,
                        jobId, trackId, (int)TrackTypeEnum.Store, false, (int)pos);
                }
                else
                {
                    AddSchedule(jobStoreSlot.StartTime + DPathTime, jobStoreSlot.EndTime - jobStoreSlot.StartTime - 2 * DPathTime,
                        jobId, trackId, (int)TrackTypeEnum.Store, false, (int)pos);
                }

                return true;
            }

            return false;
        }

        private List<EmptySlot> GetEmptySlotsForJobId(List<Schedule> nowSchedules, int jobId, int startTime, int endTime)
        {
            var emptySlots = new List<EmptySlot>();
            if (nowSchedules.Count == 0)
            {
                emptySlots.Add(new EmptySlot { MachId = jobId, StartTime = startTime, EndTime = endTime });
            }
            else
            {
                var preEmptyTime = startTime;
                foreach (var s in nowSchedules)
                {
                    var midSlot = new EmptySlot { MachId = jobId, StartTime = preEmptyTime, EndTime = s.StartTime };
                    emptySlots.Add(midSlot);
                    preEmptyTime = s.EndTime;
                }

                emptySlots.Add(new EmptySlot { MachId = jobId, StartTime = preEmptyTime, EndTime = endTime });
            }

            return emptySlots;
        }

        /// <summary>
        /// 当前任务全部调度方案，按照时间排序
        /// </summary>
        /// <param name="jobId"></param>
        /// <returns></returns>
        private List<Schedule> GetSchedulesByJobId(int jobId)
        {
            return ScheduleList.Where(x => x.JobId == jobId).OrderBy(y => y.StartTime).ToList();
        }

        /// <summary>
        /// 解码，获取调度方案
        /// 根据编码计算任务排布方案
        /// 包括
        /// 1检修计划和前置洗车计划的时间块，已知计划股道及安排顺序
        /// 2存车时间块及股道，已知计划时间
        /// 3计算适应度公式，最小调车
        /// </summary>
        private void GetSchedule()
        {
            var chromosomeList = GetGenes().Select(x => x.Value).Cast<int>().ToList();
            NotScheduleRecord = new List<string>();

            //循环编码下半区进行解码
            for (int i = 0; i < JobAndProcCnt; i++)
            {
                var nowIndex = chromosomeList[i + JobAndProcCnt];//按照编码下半区的顺序安排任务
                var jobAndProc = DataManageInstance.JobAndProcList[nowIndex];//获得任务对象
                var nowJobId = jobAndProc.JobInfo.JobId;
                var nowProcId = jobAndProc.Proc.Index;
                var track = jobAndProc.Proc.Tracks[chromosomeList[jobAndProc.Index]];//根据股道编码获得股道对象
                var nowMachId = track.Index;
                var jobAndProcWithPosList = jobAndProcWithPosDictionary[nowMachId];
                var jobAndProcWithPos = jobAndProcWithPosList.First(x => x.JobAndProcs.Contains(jobAndProc));//取得台位计划

                if (jobAndProcWithPos.TrainNum == 1)//1个车占用股道
                {
                    var pos = GetFirstShortTrainPos(track, jobAndProc.JobInfo.TrainGroupType);
                    FindSchedule(jobAndProc, track, pos);
                }
                else if (jobAndProcWithPos.TrainNum == 2)//2个车占用股道
                {
                    var jobAndProc1 = jobAndProcWithPos.JobAndProcs[0];
                    if (jobAndProc1.Index == jobAndProc.Index)//只根据排在前面的车组，同时计算两个计划
                    {
                        var pos1 = GetFirstShortTrainPos(track);
                        var pos2 = GetSecondShortTrainPos(track);
                        var jobAndProc2 = jobAndProcWithPos.JobAndProcs[1];
                        FindSchedule(jobAndProc, track, pos1, true, jobAndProc2, pos2);
                        //FindSchedule(jobAndProc2, track, pos2);
                    }
                }
            }
        }

        private bool FindSchedule(JobAndProc jobAndProc, Track track, TrainInTrackPosEnum pos = TrainInTrackPosEnum.Long, bool twoTrains = false, JobAndProc jobAndProc2 = null, TrainInTrackPosEnum pos2 = TrainInTrackPosEnum.Long)
        {
            var find = false;
            var nowMachId = track.Index;
            var nowJobId = jobAndProc.JobInfo.JobId;
            var jobPTime = twoTrains ? jobAndProc.ProcTime + SameTrackWindow : jobAndProc.ProcTime;
            //var emptySlots = GetEmptySlotsForMachIdAndPosByGetSchedules(nowMachId); //依据分配股道的空闲情况计算时间块
            var emptySlots = twoTrains ? GetEmptySlotsForMachIdAndPosByGetSchedules(nowMachId) : GetEmptySlotsForMachIdAndPosByGetSchedules(nowMachId, pos);//依据分配股道的空闲情况计算时间块
            var jobStart = twoTrains ? Math.Max(jobPreEndInfos[jobAndProc.JobInfo.JobId].JobPreEndTime, jobPreEndInfos[jobAndProc2.JobInfo.JobId].JobPreEndTime) : jobPreEndInfos[jobAndProc.JobInfo.JobId].JobPreEndTime;
            foreach (var e in emptySlots)
            {
                if (find) break;

                #region 处理调车

                if (jobPreEndInfos[nowJobId].JobAndProc != null) jobStart += DPathTime; //判断前方存在检修任务，增加调车时间
                var midCanStartTime = Math.Max(jobStart, e.StartTime + SafeTime + 2 * DPathTime); //取得当前任务的最后结束时间(+2个调车时间)和空闲时间块的最大值

                #endregion

                //判断是否可以容纳此任务的执行时间，放置任务的时候把前后调车时间预留出来
                if (e.EndTime - midCanStartTime < jobPTime) continue;
                //需要放置前置任务
                var preProcTime = twoTrains ? jobAndProc.PreProcTime * 2 : jobAndProc.PreProcTime;//排列两个洗车
                if (jobAndProc.PreProc != null && preProcTime > 0)
                {
                    find = GetJobPreProcSchedule(jobAndProc, jobPTime, e, midCanStartTime, nowMachId, preProcTime, pos, twoTrains, jobAndProc2, pos2);
                }
                else
                {
                    //什么情况不需要计算计划：（1）已经在检修计划中规定信息
                    if (ScheduleList.Exists(x => x.JobId == nowJobId && x.ProcId == jobAndProc.Proc.Index))
                    {
                        find = true;
                        break;
                    }

                    //任务前后都有调车，任务时间需要在调车后开始
                    jobPreEndInfos[nowJobId].JobPreEndTime = AddSchedule(midCanStartTime, jobPTime, nowJobId, nowMachId, jobAndProc.Proc.Index, false, (int)pos);
                    find = true;
                    break;
                }
            }

            if (!find)
            {
                NotScheduleRecord.Add($"{nowJobId}-{jobAndProc.Proc.Index}-{nowMachId}");
            }

            return find;
        }

        private static TrainInTrackPosEnum GetFirstShortTrainPos(Track track, TrainGroupTypeEnum trainGroupType = TrainGroupTypeEnum.Short)
        {
            var pos = TrainInTrackPosEnum.Long;
            if (track.TrackPosEnum == TrackPosEnum.FirstAndSecond && trainGroupType == TrainGroupTypeEnum.Short)
            {
                pos = TrainInTrackPosEnum.Pos2;
            }
            else if (track.TrackPosEnum == TrackPosEnum.SecondAndFirst && trainGroupType == TrainGroupTypeEnum.Short)
            {
                pos = TrainInTrackPosEnum.Pos1;
            }

            return pos;
        }

        private static TrainInTrackPosEnum GetSecondShortTrainPos(Track track, TrainGroupTypeEnum trainGroupType = TrainGroupTypeEnum.Short)
        {
            var pos = TrainInTrackPosEnum.Long;
            if (track.TrackPosEnum == TrackPosEnum.FirstAndSecond && trainGroupType == TrainGroupTypeEnum.Short)
            {
                pos = TrainInTrackPosEnum.Pos1;
            }
            else if (track.TrackPosEnum == TrackPosEnum.SecondAndFirst && trainGroupType == TrainGroupTypeEnum.Short)
            {
                pos = TrainInTrackPosEnum.Pos2;
            }

            return pos;
        }

        private List<EmptySlot> GetEmptySlotsForMachIdByGetSchedules(int nowMachId)
        {
            //当前股道全部调度方案，按照时间排序
            var nowSchedules = GetSchedulesByMachId(nowMachId);
            //计算该股道的空闲时间
            return GetEmptySlotsForMachId(nowSchedules, nowMachId);
        }

        private List<EmptySlot> GetEmptySlotsForMachIdAndPosByGetSchedules(int nowMachId, TrainInTrackPosEnum pos = TrainInTrackPosEnum.Long)
        {
            //当前股道全部调度方案，按照时间排序
            var nowSchedules = GetSchedulesByMachIdAndPos(nowMachId, pos);
            //计算该股道的空闲时间
            return GetEmptySlotsForMachIdAndPos(nowSchedules, nowMachId);
        }

        /// <summary>
        /// 带有前置任务的任务
        /// 获取调度方案
        /// </summary>
        /// <param name="jobAndProc"></param>
        /// <param name="find"></param>
        /// <param name="e"></param>
        /// <param name="jobPTime"></param>
        /// <param name="midCanStartTime"></param>
        /// <param name="nowJobId"></param>
        /// <param name="nowMachId"></param>
        /// <returns></returns>
        private bool GetJobPreProcSchedule(JobAndProc jobAndProc, int jobPTime, EmptySlot e, int midCanStartTime, int nowMachId, int preProcTime, TrainInTrackPosEnum pos = TrainInTrackPosEnum.Long, bool twoTrains = false, JobAndProc jobAndProc2 = null, TrainInTrackPosEnum pos2 = TrainInTrackPosEnum.Long)
        {
            var find = false;
            var preProcTracks = jobAndProc.PreProc.Tracks;//前置工序，洗车线

            #region 洗车线的均衡使用

            var tracksSchedule = preProcTracks.Select((x, y) => new { GetSchedulesByMachId(x.Index).Count, y }).OrderBy(z => z.Count).ToList();
            var trackId = preProcTracks[tracksSchedule[0].y].Index;
            find = FindPreProcScheduleForTrackId(jobAndProc, e, jobPTime, midCanStartTime, nowMachId, trackId, preProcTime, pos, twoTrains, jobAndProc2, pos2);

            #endregion

            #region 使用股道配置顺序的排布

            if (!find)
            {
                foreach (var t in preProcTracks)
                {
                    if (find) continue;
                    find = FindPreProcScheduleForTrackId(jobAndProc, e, jobPTime, midCanStartTime, nowMachId, t.Index, preProcTime, pos, twoTrains, jobAndProc2, pos2);
                }
            }

            #endregion

            return find;
        }

        private bool FindPreProcScheduleForTrackId(JobAndProc jobAndProc, EmptySlot e, int jobPTime, int midCanStartTime, int nowMachId, int trackId, int preProcTime, TrainInTrackPosEnum pos = TrainInTrackPosEnum.Long, bool twoTrains = false, JobAndProc jobAndProc2 = null, TrainInTrackPosEnum pos2 = TrainInTrackPosEnum.Long)
        {
            var find = false;

            #region 检查是否已经提前添加

            var schedules = GetSchedulesByJobId(jobAndProc.JobInfo.JobId);
            var exist = schedules.FirstOrDefault(x => x.JobId == jobAndProc.JobInfo.JobId && x.ProcId == jobAndProc.Proc.Index);//在初始化中已经添加了
            if (exist != null)
            {
                //洗车任务时间排入方案
                AddSchedule(exist.StartTime - preProcTime - DPathTime, preProcTime, jobAndProc.JobInfo.JobId, trackId, jobAndProc.PreProc.Index);

                ////检修任务排入方案
                //jobPreEndTime[nowJobId] = AddSchedule(exist.StartTime, jobPTime, nowJobId, nowMachId, jobAndProc.Proc.Index);
                return true;
            }

            #endregion

            //var emptySlotsPath = GetEmptySlotsForMachId(PathScheduleList.Values.OrderBy(x => x.StartTime).ToList(), 0);
            var emptySlots = GetEmptySlotsForMachIdByGetSchedules(trackId);

            //foreach (var emptySlotPath in emptySlotsPath)
            {
                foreach (var emptySlot in emptySlots)
                {
                    int timePos;
                    find = TimeHelper.GetEarliestTimePos(emptySlot.StartTime + 2 * DPathTime, emptySlot.EndTime, e.StartTime + 2 * DPathTime,
                        e.EndTime, preProcTime, jobPTime, midCanStartTime, out timePos, DPathTime);

                    //find = TimeHelper.GetEarliestTimePos(emptySlotsPath.Select(x => x.StartTime).ToList(), SafeTime, emptySlot.StartTime + 2 * DPathTime, emptySlot.EndTime, e.StartTime + 2 * DPathTime,
                    //    e.EndTime, DPathTime, jobAndProc.PreProcTime, jobPTime, midCanStartTime, out timePos, DPathTime);

                    if (!find) continue;


                    //洗车任务时间排入方案
                    if (twoTrains)
                    {
                        AddSchedule(timePos - preProcTime - DPathTime, preProcTime, jobAndProc.JobInfo.JobId, trackId,
                            jobAndProc.PreProc.Index, true);
                        AddSchedule(timePos - preProcTime - DPathTime, preProcTime, jobAndProc2.JobInfo.JobId, trackId,
                            jobAndProc.PreProc.Index, true);
                    }
                    else
                    {
                        AddSchedule(timePos - preProcTime - DPathTime, preProcTime, jobAndProc.JobInfo.JobId, trackId,
                            jobAndProc.PreProc.Index, true);
                    }



                    //var pos = GetFirstShortTrainPos(jobAndProc.Proc.Tracks.First(x => x.Index == nowMachId), jobAndProc.JobInfo.TrainGroupType);
                    //检修任务排入方案
                    //jobPreEndInfos[nowJobId].JobPreEndTime = AddSchedule(timePos, jobPTime, nowJobId, nowMachId, jobAndProc.Proc.Index, true, pos);
                    if (twoTrains)
                    {
                        var first = DataManageInstance.Tracks.First(x => x.Index == nowMachId);
                        jobPreEndInfos[jobAndProc.JobInfo.JobId].JobPreEndTime = AddSchedule(timePos, jobPTime, jobAndProc.JobInfo.JobId, nowMachId, jobAndProc.Proc.Index, false, (int)GetFirstShortTrainPos(first));
                        jobPreEndInfos[jobAndProc2.JobInfo.JobId].JobPreEndTime = AddSchedule(timePos, jobPTime, jobAndProc2.JobInfo.JobId, nowMachId, jobAndProc2.Proc.Index, false, (int)GetSecondShortTrainPos(first));
                    }
                    else
                    {
                        jobPreEndInfos[jobAndProc.JobInfo.JobId].JobPreEndTime = AddSchedule(timePos, jobPTime, jobAndProc.JobInfo.JobId, nowMachId, jobAndProc.Proc.Index, false, (int)pos);
                    }

                    break;
                }
            }

            return find;
        }

        /// <summary>
        /// 根据计算增加调度任务
        /// </summary>
        /// <param name="midCanStartTime"></param>
        /// <param name="jobPTime"></param>
        /// <param name="nowJobId"></param>
        /// <param name="nowMachId"></param>
        /// <param name="procId"></param>
        /// <param name="pos"></param>
        /// <param name="addPath"></param>
        /// <returns></returns>
        private int AddSchedule(int midCanStartTime, int jobPTime, int nowJobId, int nowMachId, int procId, bool addPath = false, int pos = 0)
        {
            if (jobPTime == 0) return midCanStartTime;
            var myEndTime = midCanStartTime + jobPTime;
            var midSch = new Schedule
            {
                JobId = nowJobId,
                MachId = nowMachId,
                ProcId = procId,
                StartTime = midCanStartTime,
                EndTime = myEndTime,
                PosId = pos
            };

            ScheduleList.Add(midSch);

            if (addPath)
            {
                var tmp = new Schedule()
                {
                    JobId = 0,
                    MachId = 0,
                    ProcId = 0,
                    StartTime = midCanStartTime - DPathTime,
                    EndTime = midCanStartTime
                };

                var str = tmp.ToString();
                //if (!PathScheduleList.ContainsKey(str))
                //{
                //    PathScheduleList.Add(str, tmp);
                //}

                tmp = new Schedule()
                {
                    JobId = 0,
                    MachId = 0,
                    ProcId = 0,
                    StartTime = myEndTime,
                    EndTime = myEndTime + DPathTime
                };

                str = tmp.ToString();
                //if (!PathScheduleList.ContainsKey(str))
                //{
                //    PathScheduleList.Add(str, tmp);
                //}
            }

            return myEndTime;
        }

        /// <summary>
        /// 维护任务结束时间
        /// </summary>
        /// <returns></returns>
        private void GetJobPreEndTime()
        {
            foreach (var jobInfo in DataManageInstance.JobInfoDic)
            {
                //var key = jobInfo.Key;
                var value = jobInfo.Value;
                //根据开行计划的时间窗初始化，任务结束时间
                jobPreEndInfos.Add(value.JobId, new JobPreEndInfo
                {
                    JobPreEndTime = Math.Max(value.StartTime, WindowRepairStart),
                });
            }
        }

        /// <summary>
        /// 计算该股道的空闲时间
        /// </summary>
        /// <param name="nowSchedules"></param>
        /// <param name="nowMachId"></param>
        /// <returns></returns>
        private List<EmptySlot> GetEmptySlotsForMachId(List<Schedule> nowSchedules, int nowMachId)
        {
            var emptySlots = new List<EmptySlot>();
            if (nowSchedules.Count == 0)
            {
                emptySlots.Add(new EmptySlot { MachId = nowMachId, StartTime = WindowStart, EndTime = WindowEnd });
            }
            else
            {
                var preEmptyTime = 0;
                foreach (var s in nowSchedules)
                {
                    var midSlot = new EmptySlot { MachId = nowMachId, StartTime = preEmptyTime, EndTime = s.StartTime };
                    emptySlots.Add(midSlot);
                    preEmptyTime = s.EndTime;
                }

                emptySlots.Add(new EmptySlot { MachId = nowMachId, StartTime = preEmptyTime, EndTime = WindowEnd });
            }

            return emptySlots;
        }

        private List<EmptySlot> GetEmptySlotsForMachIdAndPos(List<Schedule> nowSchedules, int nowMachId)
        {
            var emptySlots = new List<EmptySlot>();
            //if (nowSchedules.Count == 0)
            //{
            //    emptySlots.Add(new EmptySlot { MachId = nowMachId, StartTime = WindowStart, EndTime = WindowEnd });
            //}
            //else
            //{
            //    var preEmptyTime = 0;
            //    foreach (var s in nowSchedules)
            //    {
            //        var midSlot = new EmptySlot { MachId = nowMachId, StartTime = preEmptyTime, EndTime = s.StartTime };
            //        emptySlots.Add(midSlot);
            //        preEmptyTime = s.EndTime;
            //    }

            //    emptySlots.Add(new EmptySlot { MachId = nowMachId, StartTime = preEmptyTime, EndTime = WindowEnd });
            //}

            var timeWindow = new byte[WindowEnd - WindowStart];
            foreach (var nowSchedule in nowSchedules)
            {
                for (int i = nowSchedule.StartTime; i < nowSchedule.EndTime; i++)
                {
                    timeWindow[i] = 1;
                }
            }

            var preEmptyTime = WindowStart;
            var emptyFlag = true;
            for (int i = 0; i < timeWindow.Length; i++)
            {
                if (emptyFlag && timeWindow[i] == 1)
                {
                    emptySlots.Add(new EmptySlot { MachId = nowMachId, StartTime = preEmptyTime, EndTime = i });
                    emptyFlag = false;
                }
                else if (!emptyFlag && timeWindow[i] == 0)
                {
                    preEmptyTime = i;
                    emptyFlag = true;
                }
            }

            if (emptyFlag)
            {
                emptySlots.Add(new EmptySlot { MachId = nowMachId, StartTime = preEmptyTime, EndTime = WindowEnd });
            }

            return emptySlots;
        }

        /// <summary>
        /// 当前股道全部调度方案，按照时间排序
        /// </summary>
        /// <param name="nowMachId"></param>
        /// <returns></returns>
        private List<Schedule> GetSchedulesByMachId(int nowMachId)
        {
            return ScheduleList.Where(x => x.MachId == nowMachId).OrderBy(y => y.StartTime).ToList();
        }

        private List<Schedule> GetSchedulesByMachIdAndPos(int nowMachId, TrainInTrackPosEnum posId)
        {
            if (posId == TrainInTrackPosEnum.Long)
            {
                return ScheduleList.Where(x => x.MachId == nowMachId).OrderBy(y => y.StartTime).ToList();
            }
            else
            {
                return ScheduleList.Where(x => x.MachId == nowMachId && (x.PosId == (int)posId || x.PosId == (int)TrainInTrackPosEnum.Long)).OrderBy(y => y.StartTime).ToList();
            }
        }
    }
}